/* tcp.c */

#include <string.h>

#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

#include "tcp.h"

int tcp_disable_delay(int sock)
{
  int flag = 1;

  if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) < 0)
    return -1;
  return 0;
}

int tcp_set_reuseaddr(int sock)
{
  int flag = 1;

  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0)
    return -1;
  return 0;
}

int tcp_open_server(net_u32 ip, int port, int reuse_addr)
{
  int sock;
  struct sockaddr_in addr;

  sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sock < 0)
    return -1;

  if (reuse_addr)
    tcp_set_reuseaddr(sock);

  memset((char *) &addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  addr.sin_addr.s_addr = (ip == 0xffffffffu) ? htonl(INADDR_ANY) : htonl(ip);
  if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    close(sock);
    return -1;
  }

  if (listen(sock, 10) < 0) {
    close(sock);
    return -1;
  }

  return sock;
}

int tcp_accept(int sock, net_u32 *ip, int *port)
{
  struct sockaddr_in src;
  socklen_t len;
  int fd;

  len = sizeof(src);
  fd = accept(sock, (struct sockaddr *) &src, &len);
  if (fd < 0)
    return -1;

  if (ip)
    *ip = ntohl(src.sin_addr.s_addr);
  if (port)
    *port = ntohs(src.sin_port);
  return fd;
}

int tcp_open_client(int local_port, net_u32 ip, int port)
{
  int sock;
  struct sockaddr_in addr;

  sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sock < 0)
    return -1;

  if (local_port > 0) {
    memset((char *) &addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(local_port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
      close(sock);
      return -1;
    }
  }

  memset((char *) &addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  addr.sin_addr.s_addr = htonl(ip);
  if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    close(sock);
    return -1;
  }

  return sock;
}

int tcp_send(int sock, void *data, int len)
{
  int sent;
  unsigned char *p;

  p = data;
  sent = 0;
  while (sent < len) {
    int w = write(sock, p + sent, len - sent);
    if (w < 0)
      return -1;
    if (w == 0)
      break;
    sent += w;
    p += w;
  }
  return sent;
}

int tcp_send_now(int sock, void *data, int len)
{
  return write(sock, data, len);
}

int tcp_recv(int sock, void *data, int len)
{
  return read(sock, data, len);
}

void tcp_close(int sock)
{
  close(sock);
}
